package com.clp.protocol.iec104.iec104.client;

import com.clp.protocol.core.async.NettyAsyncSupport;
import com.clp.protocol.core.client.NettyClient;
import com.clp.protocol.iec104.iec104.apdu.Apdu;
import com.clp.protocol.iec104.iec104.apdu.asdu.IAsdu;
import com.clp.protocol.iec104.iec104.client.config.MasterConfig;
import com.clp.protocol.iec104.iec104.client.state.InMasterStateManager;
import com.clp.protocol.iec104.iec104.definition.ConstVal;
import com.clp.util.event.EventListenerRegister;
import io.netty.channel.Channel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;

@Slf4j
public class InMaster extends InMasterInfo implements Master {
    @Getter
    private final NettyClient nettyClient;
    private volatile Channel channel;

    private final int maxReconnectCount;
    private final boolean isAutoStartDtV;

    private final int maxAckNum; // 最大累计确认数
    private volatile int sendSeq = 0; // 发送序号，最大值32768-1
    @Setter
    private volatile int recvSeq = 0; // 接收序号，最大值32768-1
    @Setter
    private volatile int ackSeq = 0; // 发送出去的已被接收到的序号
    private volatile int currRecvCount = 0; // 当前累计收到I帧数
    private final InMasterStateManager masterStateManager;

    private final InMasterApduSender apduSender;
    private final InMasterApduRecver apduRecver;
    @Getter
    private final InMasterApduSendHandler sendHandler;
    @Getter
    private final InMasterApduRecvHandler recvHandler;
    @Getter
    private final InMasterConnector connector;

    protected InMaster(NettyClient nettyClient, MasterConfig cfg) {
        super(cfg);
        this.nettyClient = nettyClient;
        this.maxReconnectCount = cfg.getMaxReconnectCount();
        this.isAutoStartDtV = cfg.isAutoStartDtV();
        // 最大确认帧数
        this.maxAckNum = cfg.getMaxAckNum();
        this.masterStateManager = new InMasterStateManager(this, cfg);
        this.apduSender = new InMasterApduSender(this);
        this.apduRecver = new InMasterApduRecver(this);
        this.sendHandler = new InMasterApduSendHandler(this);
        this.recvHandler = new InMasterApduRecvHandler(this);
        this.connector = new InMasterConnector(this, cfg.getSupplier());
        resetSeqs();
    }

    protected void resetSeqs() {
        this.sendSeq = 0;
        this.recvSeq = 0;
        this.ackSeq = 0;
        this.currRecvCount = 0;
    }

    /**
     * 循环增加累计接收I帧数。以累计最大接收数进行循环
     */
    protected synchronized void addCurrRecvCountCircularly() {
        this.currRecvCount = (currRecvCount + 1) % maxAckNum;
    }

    /**
     * 重置累计确认数为0
     */
    public synchronized void resetCurrRecvCount() {
        this.currRecvCount = 0;
    }

    protected boolean needSendSApduOfAck() {
        return currRecvCount == 0;
    }

    protected boolean needResetCurrRecvCountByRecving(Apdu apdu) {
        if (!apdu.isIType()) {
            return false;
        }
        IAsdu iAsdu = apdu.castToIType().getIAsdu();
        switch (iAsdu.getTypeTag()) {
            case C_IC_NA_1: // 总召唤
                switch (iAsdu.getCot().getCause()) {
                    case COT_ACTTERM: // 激活终止
                        return true;
                }
                break;
        }
        return false;
    }

    public void resetChannel(Channel channel) {
        InetSocketAddress ipSocket = (InetSocketAddress) channel.remoteAddress();
        if ((this.remoteHost() != null && !this.remoteHost().equals(ipSocket.getHostString()))
                || (this.remotePort() != ipSocket.getPort())) {
            throw new RuntimeException("不能重置为不同ip:port的连接！");
        }
        this.channel = channel;
    }

    @Override
    public int sendSeq() {
        return sendSeq;
    }

    public void addSendSeqCircularly() {
        this.sendSeq = (sendSeq + 1) % ConstVal.MAX_SEQ;
    }

    @Override
    public int recvSeq() {
        return recvSeq;
    }

    @Override
    public int currRecvCount() {
        return currRecvCount;
    }

    @Override
    public NettyAsyncSupport getAsyncSupport() {
        return nettyClient;
    }

    protected Channel channel() {
        if (!isConnected()) {
            throw new IllegalStateException("Channel is not activated");
        }
        return channel;
    }

    @Override
    public boolean isConnected() {
        return channel != null && channel.isOpen() && channel.isActive();
    }

    @Override
    public boolean isAutoStartDtV() {
        return isAutoStartDtV;
    }

    @Override
    public int maxReconnectCount() {
        return maxReconnectCount;
    }

    @Override
    public InMasterStateManager stateInfo() {
        return masterStateManager;
    }

    @Override
    public EventListenerRegister<Master> getEventListenerRegister() {
        return connector.getEventPublisher();
    }

    @Override
    public InMasterApduSender getApduSender() {
        return apduSender;
    }

    @Override
    public InMasterApduRecver getApduRecver() {
        return apduRecver;
    }
}
